package main

import (
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"github.com/hyperledger/fabric/core/chaincode/shim/ext/cid"
	"github.com/hyperledger/fabric/protos/peer"
	"log"
)

type LeaseContract struct {
	LandLord  string  `json:"landlord"`
	Tenant   string  `json:"tenant"`
	MonthlyRental  int `json:"monthlyRental"`
	Tenancy  int  `json:"tenancy"`
	StartDate string `json:"startDate"`
	PartASign []byte `json:"partASign"`
	PartBSign []byte `json:"partBSign"`
}

func (lc LeaseContract) Init(stub shim.ChaincodeStubInterface) peer.Response {
	return shim.Success(nil)
}

func (lc LeaseContract) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
	fcn, args := stub.GetFunctionAndParameters()
	switch fcn {
	case "initiate":
		return initiate(stub, args)
	case "view":
		return view(stub, args)
	case "confirm":
		return confirm(stub, args)
	default:
		return shim.Error("invalid function:"+fcn)
	}
}

func createContractNo(data []byte) string {
	hasher := sha256.New()
	hasher.Write(data)
	contractNo := hasher.Sum(nil)
	return fmt.Sprintf("%x", contractNo)
}

func initiate(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	var lc LeaseContract
	clientID, _ := cid.New(stub)
	mspid, _ := clientID.GetMSPID()
	if mspid != "Org1MSP" {
		return shim.Error("launcher of lease contract must be sign by Org1")
	}
	contactBody := []byte(args[0])
	err := json.Unmarshal(contactBody, &lc)
	if err != nil {
		return shim.Error("unmarshal" + err.Error())
	}
	err = clientID.AssertAttributeValue("name", lc.LandLord)
	if err != nil {
		return shim.Error("client name not match")
	}
	contractNo := createContractNo(contactBody)
	fmt.Println("contract no:", contractNo)
	data, _ := stub.GetState(contractNo)
	if len(data) > 0 {
		return shim.Error("contract has exist")
	}
	signProp, _ := stub.GetSignedProposal()
	lc.PartASign = signProp.Signature
	data, err = json.Marshal(lc)
	if err != nil {
		return shim.Error("marshal:" + err.Error())
	}
	stub.PutState(contractNo, data)
	return shim.Success([]byte(contractNo))
}

func view(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	contractNo := args[0]
	data, err := stub.GetState(contractNo)
	if err != nil {
		return shim.Error("not found: " + err.Error())
	}
	var lc LeaseContract
	err = json.Unmarshal(data, &lc)
	if err != nil {
		return shim.Error("unmarshal:" + err.Error())
	}
	clientID, _ := cid.New(stub)
	mspId, _ := clientID.GetMSPID()
	if mspId == "Org1MSP" {
		err = clientID.AssertAttributeValue("name", lc.LandLord)
	} else if mspId == "Org2MSP" {
		err = clientID.AssertAttributeValue("name", lc.Tenant)
	}
	if err != nil {
		return shim.Error("client name not match:"+err.Error())
	}
	return shim.Success(data)
}

func confirm(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	contractNo := args[0]
	clientID, _ := cid.New(stub)
	mspid, _ := clientID.GetMSPID()
	if mspid != "Org2MSP" {
		return shim.Error("launcher of lease contract must be sign by Org2")
	}
	data, err := stub.GetState(contractNo)
	if err != nil {
		return shim.Error("not found:" + err.Error())
	}
	var lc LeaseContract
	err = json.Unmarshal(data, &lc)
	if err != nil {
		return shim.Error("unmarshal:"+err.Error())
	}
	if len(lc.PartBSign) > 0 {
		return shim.Error("contract has signed by part B")
	}
	err = clientID.AssertAttributeValue("name", lc.Tenant)
	if err != nil {
		return shim.Error("confirm user not match" + err.Error())
	}
	signProp, _ := stub.GetSignedProposal()
	lc.PartBSign = signProp.Signature
	data, err = json.Marshal(lc)
	if err != nil {
		return shim.Error("marshal:"+err.Error())
	}
	stub.PutState(contractNo, data)
	return shim.Success([]byte("confirm succes"))
}

func main() {
	cc := LeaseContract{}
	if err := shim.Start(cc); err != nil {
		log.Fatal(err)
	}
}